/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.iec.edp.caf.msu.client.register;

import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.pojo.Instance;
import io.iec.edp.caf.common.JSONSerializer;
import io.iec.edp.caf.commons.exception.CAFRuntimeException;
import io.iec.edp.caf.commons.exception.entity.DefaultExceptionProperties;
import io.iec.edp.caf.commons.exception.entity.ExceptionErrorCode;
import io.iec.edp.caf.commons.exception.ExceptionLevel;
import io.iec.edp.caf.commons.runtime.CafEnvironment;
import io.iec.edp.caf.commons.utils.SpringBeanUtils;
import io.iec.edp.caf.msu.api.ServiceUnitAwareService;
import io.iec.edp.caf.msu.api.client.ServiceRegistry;
import io.iec.edp.caf.msu.api.entity.MsuProperties;
import io.iec.edp.caf.msu.api.entity.ServiceUnitInfo;
import io.iec.edp.caf.msu.api.entity.ServiceUnitRegisterInfo;
import io.iec.edp.caf.msu.api.server.MsuServiceRegistry;
import io.iec.edp.caf.tenancy.api.context.MultiTenantContextInfo;
import io.iec.edp.caf.tenancy.core.context.MultiTenantContextHolder;
import lombok.extern.slf4j.Slf4j;
import lombok.var;

import javax.annotation.PreDestroy;
import java.util.List;

/**
 * Nacos作为服务中心，实现服务注册
 *
 * @author Leon Huo
 */
@Slf4j
public class NacosRegisterImpl implements ServiceRegistry {

    private NamingService namingService;
    private MsuProperties configuration;
    private ServiceUnitAwareService suAware;

    public NacosRegisterImpl(NamingService namingService, MsuProperties configuration, ServiceUnitAwareService suAware) {
        this.namingService = namingService;
        this.configuration = configuration;
        this.suAware = suAware;
    }

    @Override
    public Boolean register(ServiceUnitRegisterInfo registerInfo) {
        log.info("ServiceCenter(Nacos) start to register su");

        //注册
        doRegister(registerInfo);

        log.info("ServiceCenter(Nacos) finish register su");
        return true;
    }

    @Override
    @PreDestroy
    public Boolean unRegister() {
        log.info("ServiceCenter(Nacos) start to unregister su");

        //组装信息并注销微服务
        ServiceUnitRegisterInfo unRegisterInfo = this.suAware.getServiceUnitRegisterInfo(configuration.getApplicationName(), configuration.getServiceName());
        doUnRegister(unRegisterInfo);

        log.info("ServiceCenter(Nacos) finish unregister su");
        return true;
    }

    @Override
    public Boolean register(String namespace, ServiceUnitRegisterInfo registerInfo) {
        log.info("ServiceCenter(Nacos) start to register su");

        //注册
        doRegister(namespace, registerInfo);

        log.info("ServiceCenter(Nacos) finish register su");
        return true;
    }

//    @Override
//    @PreDestroy
//    public Boolean unRegister(String namespace) {
//        log.info("Nacos ServiceCenter start to unregister su");
//
//        //组装信息并注销微服务
//        ServiceUnitRegisterInfo unRegisterInfo = suAware.getServiceUnitRegisterInfo(configuration.getApplicationName(), configuration.getServiceName());
//        doUnRegister(namespace, unRegisterInfo);
//
//        log.info("Nacos ServiceCenter finish unregister su");
//        return true;
//    }

    private void doRegister(ServiceUnitRegisterInfo registerInfo) {
        try {
            //获取ip和port
            String ip = CafEnvironment.getLocalIp(configuration.getIp().getPrefix());
            Integer port = CafEnvironment.getPort();
            List<ServiceUnitInfo> suList = registerInfo.getServiceUnitInfo();

            //循环注册su
            suList.forEach(x -> {
                try {
                    this.namingService.registerInstance(x.getName().toLowerCase(), ip, port);
                } catch (NacosException e) {
                    log.error("Nacos注册su:" + x + "发生异常:" + e.getMessage(), e);
                }
            });
        } catch (Throwable e) {
            throw new RuntimeException("注册su到nacos失败:" + e.getMessage(), e);
        }
    }

    private void doUnRegister(ServiceUnitRegisterInfo unRegisterInfo) {
        try {
            //获取ip和port
            var ip = CafEnvironment.getLocalIp(configuration.getIp().getPrefix());
            var port = CafEnvironment.getPort();
            List<ServiceUnitInfo> suList = unRegisterInfo.getServiceUnitInfo();

            //循环注销su
            suList.forEach(x -> {
                try {
                    this.namingService.deregisterInstance(x.getName().toLowerCase(), ip, port);
                } catch (NacosException e) {
                    log.error("从Nacos下线su:" + x + "发生异常:" + e.getMessage(), e);
                }
            });
        } catch (Throwable e) {
            throw new RuntimeException("从Nacos注销su失败:" + e.getMessage(), e);
        }
    }

    @Deprecated
    private void registerIntoNacosAndDb(ServiceUnitRegisterInfo serviceUnitRegisterInfo) {
        //这里先注册的nacos再注册的数据库，所以MicroServiceUnit直接注册进去了，后面往数据库注册时，去发现这个MicroServiceUnit是有的
        try {
            namingService.registerInstance(configuration.getServiceName(), CafEnvironment.getLocalIp(configuration.getIp().getPrefix()), CafEnvironment.getPort());
        } catch (NacosException e) {
            throw new CAFRuntimeException(DefaultExceptionProperties.SERVICE_UNIT,
                    DefaultExceptionProperties.RESOURCE_FILE,
                    ExceptionErrorCode.nacosError,
                    new String[]{},
                    e, ExceptionLevel.Error, false);
        }

        MsuServiceRegistry msuServiceRegistry = SpringBeanUtils.getBean(MsuServiceRegistry.class);
        Boolean msuRegister = msuServiceRegistry.register(serviceUnitRegisterInfo);

        if (!msuRegister) {
            throw new CAFRuntimeException(DefaultExceptionProperties.SERVICE_UNIT,
                    DefaultExceptionProperties.RESOURCE_FILE,
                    ExceptionErrorCode.serviceRegisterError,
                    new String[]{},
                    null, ExceptionLevel.Error, false);
        }
    }

    @Deprecated
    private void unRegisterIntoNacosAndDb(ServiceUnitRegisterInfo serviceUnitRegisterInfo) {
        var servcieName = configuration.getServiceName();
        var ip = CafEnvironment.getLocalIp(configuration.getIp().getPrefix());
        var port = CafEnvironment.getPort();

        try {

            try {

                namingService.deregisterInstance(configuration.getServiceName(), CafEnvironment.getLocalIp(configuration.getIp().getPrefix()), CafEnvironment.getPort());
            } catch (NacosException e) {
                throw new CAFRuntimeException("", "", "unregist：" + configuration.getServiceName() + "," + ip + "," + port + "to nacos exception", e, ExceptionLevel.Error, false);
            }

            var temp = namingService.getAllInstances(servcieName);

            //当前服务只有一个实例的情况下，才能注销掉msuregisterinfo表里的信息。
            if (temp.size() == 1) {
                //这里销毁阶段时，rest都被销毁，rpc过去会404，先暂时强制删主库数据吧
//                LinkedHashMap<String, Object> paramMap = new LinkedHashMap();
//                paramMap.put("info", serviceUnitRegisterInfo);
//                Boolean msuRegister = this.msuServerInvoker.remoteInvoke(Boolean.class, "io.iec.edp.caf.msu.api.server.MsuServiceRegistry.unRegister", paramMap);
//                if (!msuRegister) {
//                    throw new CAFRuntimeException("","","注销："+configuration.getServiceName()+","+ip+","+port+"到gspmsuregisterinfp失败",null,ExceptionLevel.Error,false);
//                }
                try {
                    //直接注册到主库
                    MultiTenantContextInfo contextInfo = new MultiTenantContextInfo();
                    contextInfo.setMasterDb(true);
                    MultiTenantContextHolder.set(contextInfo);
                    MsuServiceRegistry msu = SpringBeanUtils.getBean(MsuServiceRegistry.class);
                    msu.unRegister(serviceUnitRegisterInfo);
                } finally {
                    MultiTenantContextHolder.set(null);
                }

            }

        } catch (NacosException e) {
            log.error(e.getErrMsg(), e);
        }

    }

    //register with namespace
    private void doRegister(String namespace, ServiceUnitRegisterInfo registerInfo) {
        try {
            //获取ip和port
            String ip = CafEnvironment.getLocalIp(configuration.getIp().getPrefix());
            Integer port = CafEnvironment.getPort();
            List<ServiceUnitInfo> suList = registerInfo.getServiceUnitInfo();

            //循环注册su
            suList.forEach(x -> {
                try {
                    System.out.println("Nacos注册：" + x);
                    Instance instance = new Instance();
                    instance.setIp(ip);
                    instance.setPort(port);
                    instance.setWeight(1.0D);
                    instance.setClusterName("DEFAULT");
                    instance.addMetadata("info", JSONSerializer.serialize(x));      //依托metadata存储微服务信息
                    this.namingService.registerInstance(x.getName().toLowerCase(), namespace, instance);
                } catch (NacosException e) {
                    log.error("Nacos注册su:" + x + "发生异常:" + e.getMessage(), e);
                }
            });
        } catch (Throwable e) {
            throw new RuntimeException("注册su到nacos失败:" + e.getMessage(), e);
        }
    }

    //unregister with namespace
    private void doUnRegister(String namespace, ServiceUnitRegisterInfo unRegisterInfo) {
        try {
            //获取ip和port
            var ip = CafEnvironment.getLocalIp(configuration.getIp().getPrefix());
            var port = CafEnvironment.getPort();
            List<ServiceUnitInfo> suList = unRegisterInfo.getServiceUnitInfo();

            //循环注销su
            suList.forEach(x -> {
                try {
                    this.namingService.deregisterInstance(x.getName().toLowerCase(), namespace, ip, port);
                } catch (NacosException e) {
                    log.error("从Nacos下线su:" + x + "发生异常:" + e.getMessage(), e);
                }
            });
        } catch (Throwable e) {
            throw new RuntimeException("从Nacos注销su失败:" + e.getMessage(), e);
        }
    }
}
